#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
#include "../src/executor/trigger_system.h"
#include "../src/utils/logger.h"
#include "../include/gudb.h"

// Test trigger functions
int test_before_insert_trigger(trigger_context_t* context) {
    if (!context) return -1;
    
    printf("BEFORE INSERT trigger executed on table '%s'\n", context->table_name);
    
    // Validate new row data
    if (!context->new_row) {
        strcpy(context->error_message, "Missing new row data for INSERT");
        context->has_error = true;
        return -1;
    }
    
    return 0;
}

int test_after_insert_trigger(trigger_context_t* context) {
    if (!context) return -1;
    
    printf("AFTER INSERT trigger executed on table '%s'\n", context->table_name);
    
    // Log the insertion
    LOG_INFO("TRIGGER_TEST", "New row inserted into table '%s'", context->table_name);
    
    return 0;
}

int test_before_update_trigger(trigger_context_t* context) {
    if (!context) return -1;
    
    printf("BEFORE UPDATE trigger executed on table '%s'\n", context->table_name);
    
    // Check both old and new row data
    if (!context->old_row || !context->new_row) {
        strcpy(context->error_message, "Missing row data for UPDATE");
        context->has_error = true;
        return -1;
    }
    
    return 0;
}

int test_after_update_trigger(trigger_context_t* context) {
    if (!context) return -1;
    
    printf("AFTER UPDATE trigger executed on table '%s'\n", context->table_name);
    return 0;
}

int test_before_delete_trigger(trigger_context_t* context) {
    if (!context) return -1;
    
    printf("BEFORE DELETE trigger executed on table '%s'\n", context->table_name);
    
    // Validate old row data
    if (!context->old_row) {
        strcpy(context->error_message, "Missing old row data for DELETE");
        context->has_error = true;
        return -1;
    }
    
    return 0;
}

int test_after_delete_trigger(trigger_context_t* context) {
    if (!context) return -1;
    
    printf("AFTER DELETE trigger executed on table '%s'\n", context->table_name);
    return 0;
}

int test_error_trigger(trigger_context_t* context) {
    if (!context) return -1;
    
    strcpy(context->error_message, "Intentional trigger error for testing");
    context->has_error = true;
    return -1;
}

// Mock database and transaction
gudb* create_mock_database() {
    gudb* db = calloc(1, sizeof(gudb));
    if (db) {
        strcpy(db->data_dir, "test_trigger_db");
        db->data_fd = -1;
        db->wal_fd = -1;
        db->binlog_fd = -1;
        db->meta_fd = -1;
        db->log_fd = -1;
        db->lock_fd = -1;
    }
    return db;
}

gudb_txn* create_mock_transaction(gudb* db) {
    gudb_txn* txn = calloc(1, sizeof(gudb_txn));
    if (txn) {
        txn->xid = 12345;
        txn->db = db;
        txn->isolation_level = GU_TXN_RC;
        txn->read_only = false;
        strcpy(txn->username, "test_user");
        txn->start_time = time(NULL);
    }
    return txn;
}

void test_trigger_system_creation() {
    printf("Testing trigger system creation...\n");
    
    // Setup logger
    logger_config_t logger_config = logger_default_config();
    strcpy(logger_config.log_file_path, "test_trigger_system.log");
    logger_config.enable_console_output = true;
    
    logger_t* logger = logger_create(&logger_config);
    assert(logger != NULL);
    logger_init(logger);
    logger_set_global(logger);
    
    // Create mock database
    gudb* db = create_mock_database();
    assert(db != NULL);
    
    // Test default configuration
    trigger_system_config_t config = trigger_system_default_config();
    assert(config.max_triggers == 1000);
    assert(config.max_sql_length == 8192);
    assert(config.enable_nested_triggers == true);
    assert(config.max_nesting_level == 8);
    
    // Create trigger system
    trigger_system_t* system = trigger_system_create(&config, db);
    assert(system != NULL);
    
    // Initialize system
    int result = trigger_system_init(system, logger);
    assert(result == 0);
    assert(system->initialized == true);
    
    // Get initial statistics
    trigger_system_stats_t stats;
    result = trigger_system_get_stats(system, &stats);
    assert(result == 0);
    assert(stats.total_triggers == 0);
    assert(stats.total_executions == 0);
    
    // Cleanup
    trigger_system_destroy(system);
    free(db);
    logger_destroy(logger);
    
    printf("Trigger system creation test passed!\n\n");
}

void test_trigger_creation_and_management() {
    printf("Testing trigger creation and management...\n");
    
    // Setup
    logger_config_t logger_config = logger_default_config();
    strcpy(logger_config.log_file_path, "test_trigger_system.log");
    logger_config.enable_console_output = false;
    
    logger_t* logger = logger_create(&logger_config);
    logger_init(logger);
    logger_set_global(logger);
    
    gudb* db = create_mock_database();
    trigger_system_config_t config = trigger_system_default_config();
    trigger_system_t* system = trigger_system_create(&config, db);
    trigger_system_init(system, logger);
    
    // Test creating SQL triggers
    int result = trigger_system_create_trigger(
        system, "test_before_insert", "users", 
        TRIGGER_EVENT_INSERT, TRIGGER_TIMING_BEFORE,
        TRIGGER_ACTION_SQL, "INSERT INTO audit_log VALUES ('insert', NOW());",
        NULL);
    assert(result == 0);
    
    result = trigger_system_create_trigger(
        system, "test_after_insert", "users",
        TRIGGER_EVENT_INSERT, TRIGGER_TIMING_AFTER,
        TRIGGER_ACTION_SQL, "UPDATE stats SET insert_count = insert_count + 1;",
        NULL);
    assert(result == 0);
    
    // Test creating trigger with condition
    result = trigger_system_create_trigger(
        system, "test_conditional", "users",
        TRIGGER_EVENT_UPDATE, TRIGGER_TIMING_BEFORE,
        TRIGGER_ACTION_SQL, "SELECT 1;",
        "NEW.status != OLD.status");
    assert(result == 0);
    
    // Test duplicate trigger name (should fail)
    result = trigger_system_create_trigger(
        system, "test_before_insert", "users",
        TRIGGER_EVENT_DELETE, TRIGGER_TIMING_BEFORE,
        TRIGGER_ACTION_SQL, "SELECT 1;", NULL);
    assert(result == -1); // Should fail
    
    // Verify triggers were created
    trigger_system_stats_t stats;
    trigger_system_get_stats(system, &stats);
    assert(stats.total_triggers == 3);
    
    // Test finding triggers
    trigger_definition_t* trigger = trigger_system_find_trigger(system, "test_before_insert");
    assert(trigger != NULL);
    assert(strcmp(trigger->name, "test_before_insert") == 0);
    assert(trigger->events == TRIGGER_EVENT_INSERT);
    assert(trigger->timing == TRIGGER_TIMING_BEFORE);
    
    // Test dropping trigger
    result = trigger_system_drop_trigger(system, "test_conditional");
    assert(result == 0);
    
    // Verify trigger was dropped
    trigger = trigger_system_find_trigger(system, "test_conditional");
    assert(trigger == NULL);
    
    trigger_system_get_stats(system, &stats);
    assert(stats.total_triggers == 2);
    
    // Cleanup
    trigger_system_destroy(system);
    free(db);
    logger_destroy(logger);
    
    printf("Trigger creation and management test passed!\n\n");
}

void test_trigger_execution() {
    printf("Testing trigger execution...\n");
    
    // Setup
    logger_config_t logger_config = logger_default_config();
    strcpy(logger_config.log_file_path, "test_trigger_system.log");
    logger_config.enable_console_output = false;
    
    logger_t* logger = logger_create(&logger_config);
    logger_init(logger);
    logger_set_global(logger);
    
    gudb* db = create_mock_database();
    gudb_txn* txn = create_mock_transaction(db);
    
    trigger_system_config_t config = trigger_system_default_config();
    trigger_system_t* system = trigger_system_create(&config, db);
    trigger_system_init(system, logger);
    
    // Create test triggers (using SQL for simplicity since function pointers are complex)
    trigger_system_create_trigger(
        system, "before_insert_users", "users",
        TRIGGER_EVENT_INSERT, TRIGGER_TIMING_BEFORE,
        TRIGGER_ACTION_SQL, "INSERT INTO log VALUES ('before_insert');", NULL);
    
    trigger_system_create_trigger(
        system, "after_insert_users", "users",
        TRIGGER_EVENT_INSERT, TRIGGER_TIMING_AFTER,
        TRIGGER_ACTION_SQL, "INSERT INTO log VALUES ('after_insert');", NULL);
    
    trigger_system_create_trigger(
        system, "before_update_users", "users",
        TRIGGER_EVENT_UPDATE, TRIGGER_TIMING_BEFORE,
        TRIGGER_ACTION_SQL, "UPDATE stats SET update_count = update_count + 1;", NULL);
    
    // Create test row data
    trigger_row_data_t* new_row = trigger_row_data_create(3);
    assert(new_row != NULL);
    
    trigger_row_data_set_column(new_row, 0, "id", "123", false);
    trigger_row_data_set_column(new_row, 1, "name", "John Doe", false);
    trigger_row_data_set_column(new_row, 2, "email", "john@example.com", false);
    
    trigger_row_data_t* old_row = trigger_row_data_create(3);
    trigger_row_data_set_column(old_row, 0, "id", "123", false);
    trigger_row_data_set_column(old_row, 1, "name", "Jane Doe", false);
    trigger_row_data_set_column(old_row, 2, "email", "jane@example.com", false);
    
    // Test BEFORE INSERT trigger execution
    int result = trigger_system_execute_triggers(
        system, "users", TRIGGER_EVENT_INSERT, TRIGGER_TIMING_BEFORE,
        txn, NULL, new_row);
    printf("BEFORE INSERT execution result: %d\n", result);
    
    // Test AFTER INSERT trigger execution
    result = trigger_system_execute_triggers(
        system, "users", TRIGGER_EVENT_INSERT, TRIGGER_TIMING_AFTER,
        txn, NULL, new_row);
    printf("AFTER INSERT execution result: %d\n", result);
    
    // Test BEFORE UPDATE trigger execution
    result = trigger_system_execute_triggers(
        system, "users", TRIGGER_EVENT_UPDATE, TRIGGER_TIMING_BEFORE,
        txn, old_row, new_row);
    printf("BEFORE UPDATE execution result: %d\n", result);
    
    // Test trigger execution on non-existent table (should return 0 triggers executed)
    result = trigger_system_execute_triggers(
        system, "nonexistent", TRIGGER_EVENT_INSERT, TRIGGER_TIMING_BEFORE,
        txn, NULL, new_row);
    assert(result == 0); // No triggers should be executed
    
    // Check statistics
    trigger_system_stats_t stats;
    trigger_system_get_stats(system, &stats);
    printf("Trigger execution statistics:\n");
    printf("  Total executions: %llu\n", stats.total_executions);
    printf("  Successful: %llu\n", stats.successful_executions);
    printf("  Failed: %llu\n", stats.failed_executions);
    
    // Cleanup
    trigger_row_data_destroy(new_row);
    trigger_row_data_destroy(old_row);
    free(txn);
    trigger_system_destroy(system);
    free(db);
    logger_destroy(logger);
    
    printf("Trigger execution test passed!\n\n");
}

void test_row_data_management() {
    printf("Testing row data management...\n");
    
    // Create row data
    trigger_row_data_t* row_data = trigger_row_data_create(4);
    assert(row_data != NULL);
    assert(row_data->column_count == 4);
    
    // Set column values
    int result = trigger_row_data_set_column(row_data, 0, "id", "1", false);
    assert(result == 0);
    
    result = trigger_row_data_set_column(row_data, 1, "name", "Test User", false);
    assert(result == 0);
    
    result = trigger_row_data_set_column(row_data, 2, "email", NULL, true); // NULL value
    assert(result == 0);
    
    result = trigger_row_data_set_column(row_data, 3, "active", "true", false);
    assert(result == 0);
    
    // Test invalid column index
    result = trigger_row_data_set_column(row_data, 10, "invalid", "value", false);
    assert(result == -1);
    
    // Get column values
    const char* id_value = trigger_row_data_get_column(row_data, "id");
    assert(id_value != NULL);
    assert(strcmp(id_value, "1") == 0);
    
    const char* name_value = trigger_row_data_get_column(row_data, "name");
    assert(name_value != NULL);
    assert(strcmp(name_value, "Test User") == 0);
    
    const char* email_value = trigger_row_data_get_column(row_data, "email");
    assert(email_value == NULL); // Should be NULL
    
    const char* nonexistent = trigger_row_data_get_column(row_data, "nonexistent");
    assert(nonexistent == NULL);
    
    // Cleanup
    trigger_row_data_destroy(row_data);
    
    printf("Row data management test passed!\n\n");
}

void test_built_in_triggers() {
    printf("Testing built-in trigger functions...\n");
    
    // Setup
    gudb* db = create_mock_database();
    gudb_txn* txn = create_mock_transaction(db);
    
    // Create trigger context
    trigger_context_t* context = trigger_context_create(
        db, txn, "test_table", TRIGGER_EVENT_INSERT, TRIGGER_TIMING_BEFORE);
    assert(context != NULL);
    
    // Create row data for context
    context->new_row = trigger_row_data_create(2);
    trigger_row_data_set_column(context->new_row, 0, "id", "1", false);
    trigger_row_data_set_column(context->new_row, 1, "name", "Test", false);
    
    // Test log changes trigger
    int result = trigger_log_changes(context);
    assert(result == 0);
    
    // Test audit changes trigger
    result = trigger_audit_changes(context);
    assert(result == 0);
    
    // Test validate data trigger
    result = trigger_validate_data(context);
    assert(result == 0);
    
    // Test update timestamp trigger
    result = trigger_update_timestamp(context);
    assert(result == 0);
    
    // Test validation with missing data
    trigger_row_data_destroy(context->new_row);
    context->new_row = NULL;
    result = trigger_validate_data(context);
    assert(result == -1); // Should fail
    assert(context->has_error == true);
    
    // Cleanup
    trigger_context_destroy(context);
    free(txn);
    free(db);
    
    printf("Built-in trigger functions test passed!\n\n");
}

void test_trigger_statistics() {
    printf("Testing trigger statistics and monitoring...\n");
    
    // Setup
    logger_config_t logger_config = logger_default_config();
    strcpy(logger_config.log_file_path, "test_trigger_system.log");
    logger_config.enable_console_output = false;
    
    logger_t* logger = logger_create(&logger_config);
    logger_init(logger);
    logger_set_global(logger);
    
    gudb* db = create_mock_database();
    trigger_system_config_t config = trigger_system_default_config();
    trigger_system_t* system = trigger_system_create(&config, db);
    trigger_system_init(system, logger);
    
    // Create some triggers
    trigger_system_create_trigger(
        system, "stats_trigger_1", "table1",
        TRIGGER_EVENT_INSERT, TRIGGER_TIMING_BEFORE,
        TRIGGER_ACTION_SQL, "SELECT 1;", NULL);
    
    trigger_system_create_trigger(
        system, "stats_trigger_2", "table1",
        TRIGGER_EVENT_UPDATE, TRIGGER_TIMING_AFTER,
        TRIGGER_ACTION_SQL, "SELECT 1;", NULL);
    
    // Get initial statistics
    trigger_system_stats_t stats_before;
    trigger_system_get_stats(system, &stats_before);
    
    // Execute some triggers
    gudb_txn* txn = create_mock_transaction(db);
    trigger_row_data_t* row_data = trigger_row_data_create(1);
    trigger_row_data_set_column(row_data, 0, "id", "1", false);
    
    trigger_system_execute_triggers(
        system, "table1", TRIGGER_EVENT_INSERT, TRIGGER_TIMING_BEFORE,
        txn, NULL, row_data);
    
    trigger_system_execute_triggers(
        system, "table1", TRIGGER_EVENT_UPDATE, TRIGGER_TIMING_AFTER,
        txn, row_data, row_data);
    
    // Get updated statistics
    trigger_system_stats_t stats_after;
    trigger_system_get_stats(system, &stats_after);
    
    // Verify statistics were updated
    assert(stats_after.total_executions > stats_before.total_executions);
    assert(stats_after.total_triggers == 2);
    
    // Print comprehensive statistics
    trigger_system_print_stats(system);
    
    printf("Statistics test results:\n");
    printf("  Total triggers: %llu\n", stats_after.total_triggers);
    printf("  Total executions: %llu\n", stats_after.total_executions);
    printf("  Successful executions: %llu\n", stats_after.successful_executions);
    printf("  Failed executions: %llu\n", stats_after.failed_executions);
    printf("  Average execution time: %.2f us\n", stats_after.avg_execution_time_us);
    
    // Cleanup
    trigger_row_data_destroy(row_data);
    free(txn);
    trigger_system_destroy(system);
    free(db);
    logger_destroy(logger);
    
    printf("Trigger statistics test passed!\n\n");
}

int main() {
    printf("=== GuDB Trigger System Test ===\n\n");
    
    test_trigger_system_creation();
    test_trigger_creation_and_management();
    test_trigger_execution();
    test_row_data_management();
    test_built_in_triggers();
    test_trigger_statistics();
    
    printf("=== All trigger system tests completed successfully! ===\n");
    printf("Check the generated log file: test_trigger_system.log\n");
    
    return 0;
}